var unsaved_rows = new Array();

function computePlacement (records) {
  // Sort in order from highest placement to lowest
  var sorter = function (a, b) {
    var ret = b.finalScore - a.finalScore;
    if (0 == ret) {
      ret = b.tieBreaker0 - a.tieBreaker0;
    }
    if (0 == ret) {
      ret = b.tieBreaker1 - a.tieBreaker1;
    }
    if (0 == ret) {
      ret = b.tieBreaker2 - a.tieBreaker2;
    }
    return ret;
  }
  records.sort(sorter);

  lastSeenScore = '';
  lastGivenPlace = 0;
  for (var i = 0; i < records.length; i++) {
    var thisScore = records[i].finalScore + '_' + records[i].tieBreaker0 + '_' + records[i].tieBreaker1 + '_' + records[i].tieBreaker2;
    if (lastSeenScore == thisScore) {
      records[i].placement = lastGivenPlace;
    } else {
      lastGivenPlace = i+1;
      records[i].placement = lastGivenPlace;
      lastSeenScore = thisScore;
    }
  }
}

function convertNumeric(str) {
  var number = parseFloat(str);
  if (isNaN(number)) {
    return 0;
  } else {
    return number;
  }
}

function compute_score(scoringRecord, isNandu) {
  var finalScore = 0;
  var tieBreaker0 = 0;
  var tieBreaker1 = 0;
  var tieBreaker2 = 0;
  if (isNandu) {
    // In nandu, add up all scores, subtract all deductions.
    finalScore = (convertNumeric(scoringRecord.score0)
        + convertNumeric(scoringRecord.score1))/2
        + (convertNumeric(scoringRecord.score2)
        + convertNumeric(scoringRecord.score3))/2
        + (convertNumeric(scoringRecord.score4)
        + convertNumeric(scoringRecord.score5))/2
        - convertNumeric(scoringRecord.timeDeduction)
        - convertNumeric(scoringRecord.otherDeduction);
  } else {
      // Get all given scores
      // Ignore 0-valued scores
      var givenScores = [];
      // provide impossible scores for the initial min and max.
      var maxScore = -1;
      var minScore = 11;
      var sumScores = 0;
      var scoreFields = ['score0', 'score1', 'score2', 'score3', 'score4'];
      for (var i = 0; i < scoreFields.length; i++) {
        var score = convertNumeric(eval("scoringRecord." + scoreFields[i]));
        if (score > 0) {
          maxScore = Math.max(maxScore, score);
          minScore = Math.min(minScore, score);
          sumScores += score;
          givenScores.push(score);
        }
      }

      if (0 == givenScores.length || 3 > givenScores.length) {
        // Short-circuit the empty case.
        // If this doesn't meet the minimum judges, quit.
      } else {
        // average of scores not including the highest and lowest
        var meritedScore = (sumScores - minScore - maxScore) / (givenScores.length - 2);
        // final score is merited score minus any deductions
        finalScore = meritedScore - convertNumeric(scoringRecord.timeDeduction) - convertNumeric(scoringRecord.otherDeduction);
      }

      // Compute and set tiebreakers
      // negate so "bigger is better"
      tieBreaker0 = -1 * (Math.abs((maxScore + minScore) / 2 - finalScore));
      tieBreaker1 = (maxScore + minScore) / 2;
      tieBreaker2 = minScore;
  }

  scoringRecord.finalScore = finalScore;
  scoringRecord.tieBreaker0 = tieBreaker0;
  scoringRecord.tieBreaker1 = tieBreaker1;
  scoringRecord.tieBreaker2 = tieBreaker2;

  return finalScore;
}

(function( $ ) {
  // Save a row.  Call this on a <tr> tag containing elements to be saved
  $.fn.saveRow = function() {
    // Retrive data
    r = /[0-9]+/
    record = new Object();
    fields = new Array(this.find(".score1"),
                       this.find(".score2"),
                       this.find(".score3"),
                       this.find(".score4"),
                       this.find(".score5"),
                       this.find(".time"),
                       this.find(".time_deduct"),
                       this.find(".deduct"));
    record.id = r.exec(this.attr("id"))[0];
    record.score0 = this.find(".score1").val();
    record.score1 = this.find(".score2").val();
    record.score2 = this.find(".score3").val();
    record.score3 = this.find(".score4").val();
    record.score4 = this.find(".score5").val();
    record.score5 = 0;
    record.time = this.find(".time").val();
    record.timeDeduction = this.find(".time_deduct").val();
    record.otherDeduction = this.find(".deduct").val();
    
    // Validate data
    number_re = /^[0-9]+(\.[0-9]+)?$|\.[0-9]+/
    time_re = /^[0-9]+$/
    pass = true;
    error_message = "";
    for (index in fields) {
      field = fields[index];
      if (field.hasClass("time")) {
        field_re = time_re;
      } else {
        field_re = number_re;
      }
      if (!field.val().match(field_re)) {
        pass = false;
        field.parents("td").first().addClass("error");
        if (field.val() == "") {
          error_message += field.attr("name") + " is empty\r\n";
        } else {
          if (field.hasClass("time")) {
            error_message += field.attr("name") + " is not valid - must be in seconds\r\n";
          } else {
            error_message += field.attr("name") + " is not valid\r\n";
          }
        }
      } else {
        field.parents("td").first().removeClass("error");
      }
    }
    if (!pass) {
      update_banner(true, false, "There are errors on the page, please fix");
      alert("There are errors: \r\n\r\n" + error_message);
      return;
    } else {
      update_banner(false, false, "Saving...");
    }
    
    // Fill in the rest of the fields
    compute_score(record);
    records = new Array(record);
  
    // Upload data
    tr = this;
    $.getJSON(set_url, {
            type: 'scoring',
            op: 'edit',
            records: JSON.stringify(records)
        }, function(data) {
          if (data.success == "true") {
            tr.find(".save-button").addClass("hidden");
            tr.find(".unlock-button").removeClass("hidden");
            tr.removeClass("error-row");
            tr.next().find("input").first().focus();
            tr.find("span.view").remove();
            // Remove this row from unsaved rows
            in_array = $.inArray(tr.attr('id'), unsaved_rows);
            if (in_array != -1) {
              unsaved_rows.splice(in_array, 1);
            }
            update_banner(false, true, "Save successful");
            update();
          } else {
            update_banner(true, false, "Save unsuccessful");
          }
    });
  };
})( jQuery );

// Unlock a row for editing.  Removes <span> tags for <input> tags
function unlockRow() {
  tr = $(this).parents("tr");
  tr.find("span.view").hide();
  tr.find("input").show();
  tr.find(".unlock-button").addClass("hidden");
  tr.find(".save-button").removeClass("hidden");
};

/* Whenever the input changes, recompute final score */
function update_score() {
  $(".competitor").each(function(index){
    record = new Object();
    record.score0 = $(this).find(".score1").val();
    record.score1 = $(this).find(".score2").val();
    record.score2 = $(this).find(".score3").val();
    record.score3 = $(this).find(".score4").val();
    record.score4 = $(this).find(".score5").val();
    record.time = $(this).find(".time").val();
    record.timeDeduction = $(this).find(".time_deduct").val();
    record.otherDeduction = $(this).find(".deduct").val();
    compute_score(record);
    if (record.finalScore == "NaN") {
      $(this).find(".final").text("N/A");
    } else {
      $(this).find(".final").text(record.finalScore.toFixed(2));
    }
  });
}

/* Update all fields */
function update() {
  query = $.getJSON(get_url, {
      type: 'scoring',
      filterField: 'eventId',
      filterValue: eventId
    }, function(data) {
    records = data.records;
    old_records = records;  // Save these records for undo later
    for (index in records) {
      record = records[index];
      score_id = "#score-" + record.id;
      $(score_id + " .score1").val(record.score0);
      $(score_id + " .score2").val(record.score1);
      $(score_id + " .score3").val(record.score2);
      $(score_id + " .score4").val(record.score3);
      $(score_id + " .score5").val(record.score4);
      $(score_id + " .time").val(record.time);
      $(score_id + " .time_deduct").val(record.timeDeduction);
      $(score_id + " .deduct").val(record.otherDeduction);
      $(score_id + " .final").html(parseFloat(record.finalScore).toFixed(2));
      $(score_id + " .place").html(record.placement);
      
      // If the final score is 0, then it's probably not a valid competitor.
      // At this point their placement should read N/A rather than 1st or whatever
      if (parseFloat(record.finalScore) == 0) {
        $(score_id + " .final").html("N/A");
        $(score_id + " .place").html("N/A");
      }
      
      // Change input boxes to spans if we detect that final score is non-zero (has been saved before)
      // But only do this if we havn't done it already
      if (record.finalScore != null && ($(score_id + " span.view").length == 0)) {
        $(score_id + " .input-tiny").each(function() {
          $("<span />", { text: this.value, "class":"view" }).insertAfter(this);
          $(this).hide();
        });
        $(score_id + " .save-button").addClass("hidden");
        $(score_id + " .unlock-button").removeClass("hidden");
      }
    }
    $(".competitor input").each(function(index){
      if (completed) {
        // Disable inputs if event is completed
        $(this).addClass("disabled");
        $(this).attr("disabled", "true");
      } else {
        $(this).removeClass("disabled");
        $(this).removeAttr("disabled");
      }
    });
    
    // Update buttons
    if (completed) {
      $("#finished").removeClass("hidden");
      $(".unlock-button").attr("disabled", "disabled")
      $(".save-button").attr("disabled", "disabled")
      $(".save-button").unbind('click');
      $(".unlock-button").unbind('click');
    } else {
      $("#saved").removeClass("hidden");
      $(".unlock-button").removeAttr("disabled");
      $(".save-button").removeAttr("disabled");
      $(".save-button").click(function(){$(this).parents("tr").first().saveRow();});
      $(".unlock-button").click(unlockRow);
    }
    
    // Update Infobar
    if (unsaved_rows.length > 0) {
      update_banner(true, false, "There are " + unsaved_rows.length + " unsaved rows");
    } else {
      update_banner(false, false, "There are no unsaved changes");
      $("#finish").removeClass("disabled");
    }
  });
}

function unfinish_event() {
  $(".btn").addClass("disabled");
  $("#loading").addClass("hidden");
  $("#unsaved").addClass("hidden");
  $("#saved").addClass("hidden");
  $("#finished").addClass("hidden");
  $("#saving").removeClass("hidden");
  $.getJSON(set_url, {
    op: 'edit',
    type: 'event',
    records: JSON.stringify([
      {
        id: eventId,
        isFinished: "false"
      }
    ])
  }, function(data) {
    if (data.success == "true") {
      $(".btn").removeClass("disabled");
      $("#finish").html("Finish Event");
      $("#finish").unbind('click');
      $("#finish").click(finish_event);
      completed = false;
      update();
      $("#infobar").removeClass("alert-info");
      $("#infobar").removeClass("alert-error");
      $("#infobar").addClass("alert-success");
    } else {
      
    }
  });
}

/* Calculate placements and upload scores */
function finish_event() {
  // Make sure infobar is displaying what we want
  $(".btn").addClass("disabled");
  $("#loading").removeClass("hidden");
  $("#unsaved").addClass("hidden");
  $("#saved").addClass("hidden");
  $("#finished").addClass("hidden");
  $("#saving").addClass("hidden");
  
  // Calculate placements (1st, 2nd, etc...)
  var records = new Array();
  $("#scoring_table").find("tr").each(function(index, value){
    r = /[0-9]+/
    if (!$(value).attr("id")) return;
    record = new Object();
    fields = new Array($(value).find(".score1"),
                       $(value).find(".score2"),
                       $(value).find(".score3"),
                       $(value).find(".score4"),
                       $(value).find(".score5"),
                       $(value).find(".time"),
                       $(value).find(".time_deduct"),
                       $(value).find(".deduct"));
    // Quick validation.  Fill with 0's if not present
    for (index in fields) {
      field = fields[index];
      if (field.val() == '') {
        field.val("0");
      }
    }
    
    record.id = r.exec($(value).attr("id"))[0];
    record.score0 = $(value).find(".score1").val();
    record.score1 = $(value).find(".score2").val();
    record.score2 = $(value).find(".score3").val();
    record.score3 = $(value).find(".score4").val();
    record.score4 = $(value).find(".score5").val();
    record.score5 = 0;
    record.time = $(value).find(".time").val();
    record.timeDeduction = $(value).find(".time_deduct").val();
    record.otherDeduction = $(value).find(".deduct").val();
    
    compute_score(record);
    records.push(record);
  });
  computePlacement(records);
  
  // Upload scores and placements
  $.getJSON(set_url, {
        type: 'scoring',
        op: 'edit',
        records: JSON.stringify(records)
    }, function(data) {
      if (data.success == "true") {
        update_banner(false, true, "Save successful");
      } else {
        update_banner(true, false, "Save unsuccessful");
      }
  });

  // Finish event
  $.getJSON(set_url, {
    op: 'edit',
    type: 'event',
    records: JSON.stringify([
      {
        id: eventId,
        isFinished: "true"
      }
    ])
  }, function(data) {
    if (data.success == "true") {
      completed = true;
      update();
      $(".btn").removeClass("disabled");
      $("#infobar").removeClass("alert-info");
      $("#infobar").removeClass("alert-error");
      $("#infobar").addClass("alert-success");
      $("#finish").html("Undo Finish Event");
      $("#finish").unbind('click');
      $("#finish").click(unfinish_event);
      $("#save").addClass("disabled");
    } else {
      
    }
  });
}

// Datatables configuration
$.extend( $.fn.dataTableExt.oStdClasses, {
  "sSortAsc": "header headerSortDown",
  "sSortDesc": "header headerSortUp",
  "sSortable": "header"
} );

/* Tell Django that this event is now ongoing */
function start_event() {
  $.getJSON("/start_event/" + eventId, function() {
    ongoing = true;
    $("#start_event_modal").modal('hide');
  });
}

// Use the global variables error and message to change the banner
function update_banner(error_flag, success_flag, banner_message) {
  $("#infobar").removeClass("alert-info");
  $("#infobar").removeClass("alert-error");
  $("#infobar").removeClass("alert-success");
  if (error_flag) {
    $("#infobar").addClass("alert-error")
  } else if (success_flag) { 
    $("#infobar").addClass("alert-success");
  } else {
    $("#infobar").addClass("alert-info");
  }
  $("#infobar").text(banner_message);
}

$(document).ready(function(){
  // Add event handler to each of the text boxes
  $(".competitor input").each(function(index){
    $(this).change(function(event) {
      update_banner(true, false, "There are unsaved changes.");
      // Keep track of unsaved rows
      if ($.inArray($(this).parents("tr").first().attr('id'), unsaved_rows) == -1) {
        unsaved_rows.push($(this).parents("tr").first().attr('id'));
      }
      $("#saving").addClass("hidden");
      $("#saved").addClass("hidden");
      $("#finish").addClass("disabled");
      $(this).parents("tr").addClass("error-row");
      update_score();
    });
    
    $(this).keyup(function(event){
      if (event.keyCode == 13) {
        $(this).parents("tr").saveRow();
      }
    });
  });
  
  // Add handlers to the buttons
  $("#start_event").click(start_event);
  $(".save-button").click(function(){$(this).parents("tr").first().saveRow();});
  $(".unlock-button").click(unlockRow);
  
  update();
  
  $('#scoring_table').dataTable({
      "sDom": "<'row'<'span8'l><'span8'f>r>t<'row'<'span8'i><'span8'p>>",
      "bPaginate": false,
      "bLengthChange": false,
      "bFilter": false,
      "bSort": true,
      "bInfo": false,
      "bAutoWidth": false,
      "aaSorting": []
  });
  
  if (completed) {
    $("#finish").click(unfinish_event);
    $("#save").addClass("disabled");
  } else {
    $("#finish").click(finish_event);
    if (!ongoing) {
      // Drop a model down to start the event
      $('#start_event_modal').modal({keyboard: false, backdrop: 'static'});
    }
  }
  update_banner(false, false, "There are no unsaved changes");
});